// import com.alibaba.druid.DbType;
// import com.alibaba.druid.sql.ast.SQLExpr;
// import com.alibaba.druid.sql.ast.SQLName;
// import com.alibaba.druid.sql.ast.SQLObject;
// import com.alibaba.druid.sql.ast.expr.SQLListExpr;
// import com.alibaba.druid.sql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.mysql.ast.*;
// import com.alibaba.druid.sql.dialect.mysql.ast.clause.*;
// import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement;
// import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr;
// import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr;
// import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlUserName;
// import com.alibaba.druid.sql.dialect.mysql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement.TableSpaceOption;
// import com.alibaba.druid.sql.repository.SchemaRepository;
// import com.alibaba.druid.sql.visitor.SchemaStatVisitor;
// import com.alibaba.druid.stat.TableStat;
// import com.alibaba.druid.stat.TableStat.Mode;
#include "MySqlSchemaStatVisitor.h"
#include "../../../../DbType.h"
#include "../../../ast/SQLExpr.h"
#include "../../../ast/SQLName.h"
#include "../../../ast/SQLObject.h"
#include "../../../ast/expr/SQLListExpr.h"
#include "../../../ast/statement/SQLSelectStatement.h"
#include "../../../ast/statement/SQLExprTableSource.h"
#include "../../../ast/statement/SQLUpdateStatement.h"
#include "../../../ast/statement/SQLAlterTableStatement.h"
#include "../../../ast/statement/SQLColumnDefinition.h"
#include "../ast/expr/MySqlCharExpr.h"
#include "../ast/expr/MySqlOutFileExpr.h"
#include "../ast/expr/MySqlUserName.h"
#include "../ast/statement/MySqlCreateTableStatement.h"
#include "../ast/statement/MySqlDeleteStatement.h"
#include "../ast/statement/MySqlInsertStatement.h"
#include "../ast/statement/MySqlPartitionByKey.h"
#include "../ast/statement/MySqlExplainStatement.h"
#include "../ast/statement/MySqlUpdateStatement.h"
#include "../ast/statement/MySqlAlterTableChangeColumn.h"
#include "../ast/statement/MySqlAlterTableModifyColumn.h"
#include "../ast/statement/MySqlUpdateTableSource.h"
#include "../ast/statement/MySqlCheckTableStatement.h"
#include "../../../repository/SchemaRepository.h"
#include "../../../../stat/TableStat.h"
#include "../../../../utils/instanceof.h"
#include "../../../../utils/listTran.h"
#include "../ast/MySqlKey.h"
#include "../ast/MySqlPrimaryKey.h"
#include "../ast/clause/MySqlCaseStatement.h"
#include "../ast/clause/MySqlRepeatStatement.h"
#include "../ast/clause/MySqlCursorDeclareStatement.h"

MySqlSchemaStatVisitor::MySqlSchemaStatVisitor()
    : SchemaStatVisitor(DbType_ptr(new DbType(&DbType::mysql)))
{
}

MySqlSchemaStatVisitor::MySqlSchemaStatVisitor(SchemaRepository_ptr repository)
    : SchemaStatVisitor(repository)
{
}

bool MySqlSchemaStatVisitor::visit(SQLSelectStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    SchemaResolveVisitor_Option_list_ptr tmp = std::make_shared<std::list<SchemaResolveVisitor_Option_ptr>>();
    repository->resolve(x, tmp);
  }

  return true;
}

// @Override
DbType_ptr MySqlSchemaStatVisitor::getDbType()
{
  return DbType_ptr(new DbType(&DbType::mysql));
}

// DUAL
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlDeleteStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    SchemaResolveVisitor_Option_list_ptr tmp = std::make_shared<std::list<SchemaResolveVisitor_Option_ptr>>();
    repository->resolve(x, tmp);
  }

  SQLTableSource_ptr from = x->getFrom();
  if (from != nullptr)
  {
    from->accept(std::make_shared<MySqlSchemaStatVisitor>(*this));
  }

  SQLTableSource_ptr using_ = x->getUsing();
  if (using_ != nullptr)
  {
    using_->accept(std::make_shared<MySqlSchemaStatVisitor>(*this));
  }

  SQLTableSource_ptr tableSource = x->getTableSource();
  tableSource->accept(std::make_shared<MySqlSchemaStatVisitor>(*this));

  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    TableStat_ptr stat = this->getTableStat(std::dynamic_pointer_cast<SQLExprTableSource>(tableSource));
    stat->incrementDeleteCount();
  }

  accept(x->getWhere());

  accept(x->getOrderBy());
  accept(x->getLimit());

  return BOOL::FALSE;
}

// @Override
void MySqlSchemaStatVisitor::endVisit(MySqlInsertStatement_ptr x)
{
  SchemaStatVisitor::setModeOrigin(x);
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlInsertStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    SchemaResolveVisitor_Option_list_ptr tmp = std::make_shared<std::list<SchemaResolveVisitor_Option_ptr>>();
    repository->resolve(x, tmp);
  }

  setMode(x, TableStat_Mode::Insert);

  TableStat_ptr stat = getTableStat(x->getTableSource());

  if (stat != nullptr)
  {
    stat->incrementInsertCount();
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
  accept(tmp1);
  SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLInsertStatement_ValuesClause, SQLObject>(x->getValuesList());
  accept(tmp2);
  accept(x->getQuery());
  SQLObject_list_ptr tmp3 = ListTran::tran_class<SQLExpr, SQLObject>(x->getDuplicateKeyUpdate());
  accept(tmp3);

  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlKey_ptr x)
{
  for (SQLObject_ptr item : *x->SQLUnique::getColumns())
  {
    item->accept(std::make_shared<MySqlSchemaStatVisitor>(*this));
  }
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlPrimaryKey_ptr x)
{
  for (SQLSelectOrderByItem_ptr item : *x->SQLUnique::getColumns())
  {
    SQLExpr_ptr expr = item->getExpr();
    expr->accept(std::make_shared<MySqlSchemaStatVisitor>(*this));
  }
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlPartitionByKey_ptr x)
{
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
  accept(tmp1);
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlExplainStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    SchemaResolveVisitor_Option_list_ptr tmp = std::make_shared<std::list<SchemaResolveVisitor_Option_ptr>>();
    repository->resolve(x, tmp);
  }

  SQLName_ptr tableName = x->getTableName();
  if (tableName != nullptr)
  {
    getTableStat(tableName);

    SQLName_ptr columnName = x->getColumnName();
    if (columnName != nullptr)
    {
      addColumn(tableName, columnName->getSimpleName());
    }
  }

  if (x->getStatement() != nullptr)
  {
    accept(x->getStatement());
  }

  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlUpdateStatement_ptr x)
{
  SchemaStatVisitor::visit(std::dynamic_pointer_cast<SQLUpdateStatement>(x));
  for (SQLExpr_ptr item : *x->getReturning())
  {
    item->accept(std::make_shared<MySqlSchemaStatVisitor>(*this));
  }

  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlAlterTableChangeColumn_ptr x)
{
  SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());

  SQLName_ptr table = stmt->getName();

  SQLName_ptr column = x->getColumnName();
  string_ptr columnName = column->toString();
  addColumn(table, columnName);
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlAlterTableModifyColumn_ptr x)
{
  SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());

  SQLName_ptr table = stmt->getName();

  SQLName_ptr column = x->getNewColumnDefinition()->getName();
  string_ptr columnName = column->toString();
  addColumn(table, columnName);

  return BOOL::FALSE;
}

// @Override
bool MySqlSchemaStatVisitor::visit(MySqlCreateTableStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    SchemaResolveVisitor_Option_list_ptr tmp = std::make_shared<std::list<SchemaResolveVisitor_Option_ptr>>();
    repository->resolve(x, tmp);
  }

  bool val = SchemaStatVisitor::visit(std::dynamic_pointer_cast<SQLCreateTableStatement>(x));

  SQLExpr_ptr union_ = x->getOption(make_string_ptr("union"));
  if (instanceof <SQLExpr, SQLListExpr>(union_))
  {
    for (SQLExpr_ptr item : *(std::dynamic_pointer_cast<SQLListExpr>(union_))->getItems())
    {
      if (instanceof <SQLExpr, SQLName>(item))
      {
        getTableStatWithUnwrap(item);
      }
    }
  }

  return val;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlCaseStatement_ptr x)
{
  SQLObject_list_ptr tmp1 = ListTran::tran_class<MySqlCaseStatement_MySqlWhenStatement, SQLObject>(x->getWhenList());
  accept(tmp1);
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlCaseStatement_MySqlWhenStatement_ptr x)
{
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLStatement, SQLObject>(x->getStatements());
  accept(tmp1);
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlRepeatStatement_ptr x)
{
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLStatement, SQLObject>(x->getStatements());
  accept(tmp1);
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlCursorDeclareStatement_ptr x)
{
  accept(x->getSelect());
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlUpdateTableSource_ptr x)
{
  if (x->getUpdate() != nullptr)
  {
    return this->visit(x->getUpdate());
  }
  return BOOL::FALSE;
}

// @Override
BOOL_ptr MySqlSchemaStatVisitor::visit(MySqlCheckTableStatement_ptr x)
{
  for (SQLExprTableSource_ptr tableSource : *x->getTables())
  {
    tableSource->accept(std::make_shared<MySqlSchemaStatVisitor>(*this));
  }
  return BOOL::FALSE;
}
